package org.apache.lucene.ocean.util;

/**
 * Number encoding utility from SOLR
 *
 */
public class NumberUtils {
  public static long decode(byte[] bytes) {
    return (((long) bytes[0] << 56) + ((long) (bytes[1] & 255) << 48) + ((long) (bytes[2] & 255) << 40) + ((long) (bytes[3] & 255) << 32)
        + ((long) (bytes[4] & 255) << 24) + ((bytes[5] & 255) << 16) + ((bytes[6] & 255) << 8) + ((bytes[7] & 255) << 0));
  }

  public static byte[] encode(long v, byte[] value) {
    value[0] = (byte) (v >>> 56);
    value[1] = (byte) (v >>> 48);
    value[2] = (byte) (v >>> 40);
    value[3] = (byte) (v >>> 32);
    value[4] = (byte) (v >>> 24);
    value[5] = (byte) (v >>> 16);
    value[6] = (byte) (v >>> 8);
    value[7] = (byte) (v >>> 0);
    return value;
  }

  public static byte[] encode(long v) {
    byte[] value = new byte[8];
    return encode(v, value);
  }

  public static String int2sortableStr(int val) {
    char[] arr = new char[3];
    int2sortableStr(val, arr, 0);
    return new String(arr, 0, 3);
  }

  public static String int2sortableStr(String val) {
    return int2sortableStr(Integer.parseInt(val));
  }

  public static String sortableStr2int(String val) {
    int ival = sortableStr2int(val, 0, 3);
    return Integer.toString(ival);
  }

  public static String long2sortableStr(long val) {
    char[] arr = new char[5];
    long2sortableStr(val, arr, 0);
    return new String(arr, 0, 5);
  }

  public static String long2sortableStr(String val) {
    return long2sortableStr(Long.parseLong(val));
  }

  public static long sortableString2Long(String val) {
    long ival = sortableStr2long(val, 0, 5);
    return ival;
    // return Long.toString(ival);
  }

  public static String sortableStr2long(String val) {
    long ival = sortableStr2long(val, 0, 5);
    return Long.toString(ival);
  }

  //
  // IEEE floating point format is defined so that it sorts correctly
  // when interpreted as a signed integer (or signed long in the case
  // of a double) for positive values. For negative values, all the bits except
  // the sign bit must be inverted.
  // This correctly handles all possible float values including -Infinity and
  // +Infinity.
  // Note that in float-space, NaN<x is false, NaN>x is false, NaN==x is false,
  // NaN!=x is true
  // for all x (including NaN itself). Internal to Solr, NaN==NaN is true and
  // NaN
  // sorts higher than Infinity, so a range query of [-Infinity TO +Infinity]
  // will
  // exclude NaN values, but a query of "NaN" will find all NaN values.
  // Also, -0==0 in float-space but -0<0 after this transformation.
  //
  public static String float2sortableStr(float val) {
    int f = Float.floatToRawIntBits(val);
    if (f < 0)
      f ^= 0x7fffffff;
    return int2sortableStr(f);
  }

  public static String float2sortableStr(String val) {
    return float2sortableStr(Float.parseFloat(val));
  }

  public static float sortableStr2float(String val) {
    return sortableStr2float(val, 0, 3);
  }

  public static float sortableStr2float(String val, int off, int len) {
    int f = sortableStr2int(val, off, len);
    if (f < 0)
      f ^= 0x7fffffff;
    return Float.intBitsToFloat(f);
  }

  public static String sortableStr2floatStr(String val) {
    return Float.toString(sortableStr2float(val));
  }

  public static String double2sortableStr(double val) {
    long f = Double.doubleToRawLongBits(val);
    if (f < 0)
      f ^= 0x7fffffffffffffffL;
    return long2sortableStr(f);
  }

  public static String double2sortableStr(String val) {
    return double2sortableStr(Double.parseDouble(val));
  }

  public static double sortableStr2double(String val) {
    long f = sortableStr2long(val, 0, 6);
    if (f < 0)
      f ^= 0x7fffffffffffffffL;
    return Double.longBitsToDouble(f);
  }

  public static double sortableStr2double(String val, int offset, int length) {
    long f = sortableStr2long(val, offset, length);
    if (f < 0)
      f ^= 0x7fffffffffffffffL;
    return Double.longBitsToDouble(f);
  }

  public static String sortableStr2doubleStr(String val) {
    return Double.toString(sortableStr2double(val));
  }

  // uses binary representation of an int to build a string of
  // chars that will sort correctly. Only char ranges
  // less than 0xd800 will be used to avoid UCS-16 surrogates.
  public static int int2sortableStr(int val, char[] out, int offset) {
    val += Integer.MIN_VALUE;
    out[offset++] = (char) (val >>> 24);
    out[offset++] = (char) ((val >>> 12) & 0x0fff);
    out[offset++] = (char) (val & 0x0fff);
    return 3;
  }

  public static int sortableStr2int(String sval, int offset, int len) {
    int val = sval.charAt(offset++) << 24;
    val |= sval.charAt(offset++) << 12;
    val |= sval.charAt(offset++);
    val -= Integer.MIN_VALUE;
    return val;
  }

  // uses binary representation of an int to build a string of
  // chars that will sort correctly. Only char ranges
  // less than 0xd800 will be used to avoid UCS-16 surrogates.
  // we can use the lowest 15 bits of a char, (or a mask of 0x7fff)
  public static int long2sortableStr(long val, char[] out, int offset) {
    val += Long.MIN_VALUE;
    out[offset++] = (char) (val >>> 60);
    out[offset++] = (char) (val >>> 45 & 0x7fff);
    out[offset++] = (char) (val >>> 30 & 0x7fff);
    out[offset++] = (char) (val >>> 15 & 0x7fff);
    out[offset] = (char) (val & 0x7fff);
    return 5;
  }

  public static long sortableStr2long(String sval, int offset, int len) {
    long val = (long) (sval.charAt(offset++)) << 60;
    val |= ((long) sval.charAt(offset++)) << 45;
    val |= ((long) sval.charAt(offset++)) << 30;
    val |= sval.charAt(offset++) << 15;
    val |= sval.charAt(offset);
    val -= Long.MIN_VALUE;
    return val;
  }
}
